﻿#region Copyright(C) 2018 FI Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME	    : FI.Net Application Framework
// COMPONENT ID	    : FI.Core
// COMPONENT DESC   : FI.Net Core Foundation
//
// CREATED DATE/BY  : 18 Aug 2018 / Tian Mi
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using FI.Core.Utilities;


namespace FI.Core
{
    /// <summary>
    /// This static class is used to get attributes from assemblies, types and type members.
    /// </summary>
    public static class AttributeAccessor
    {
        #region Static Fields
        private static readonly Dictionary<object, Attribute[]> attributeCache = new Dictionary<object, Attribute[]>();
        private static readonly object syncHelper = new object();
        #endregion

        #region GetAttributes

        /// <summary>
        /// Gets the attributes applied to the given <see cref="Assembly"/>.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>
        /// The attributes applied to the given <see cref="Assembly"/>
        /// </returns>
        public static IEnumerable<Attribute> GetAttributes(Assembly assembly, bool inherit)
        {
            Guard.ArgumentNotNull(assembly, "assembly");
            return GetAttributes<Assembly>(assembly, (assembly1, inherit1) => assembly1.GetCustomAttributes(inherit1), inherit);
        }

        /// <summary>
        /// Gets the attributes applied to the given <see cref="Type"/>,
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>
        /// The attributes applied to the given <see cref="MemberInfo"/>
        /// </returns>
        public static IEnumerable<Attribute> GetAttributes(Type type, bool inherit)
        {
            Guard.ArgumentNotNull(type, "type");
            return GetAttributes<Type>(type, (type1, inherit1) => type1.GetCustomAttributes(inherit1), inherit);
        }

        /// <summary>
        /// Gets the attributes applied to the given <see cref="MemberInfo"/>.
        /// </summary>
        /// <param name="member">The member.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>
        /// The attributes applied to the given <see cref="Type"/>
        /// </returns>
        public static IEnumerable<Attribute> GetAttributes(MemberInfo member, bool inherit)
        {
            Guard.ArgumentNotNull(member, "member");
            return GetAttributes<MemberInfo>(member, (member1, inherit1) => member1.GetCustomAttributes(inherit1), inherit);
        }
        #endregion

        #region GetAttributes<TAttribute>

        /// <summary>
        /// Gets the attributes applied to the given <see cref="Assembly"/>.
        /// </summary>
        /// <typeparam name="TAttribute">The type of the attribute.</typeparam>
        /// <param name="assembly">The assembly.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>
        /// The attributes applied to the given <see cref="Assembly"/>
        /// </returns>
        public static IEnumerable<TAttribute> GetAttributes<TAttribute>(Assembly assembly, bool inherit) where TAttribute : Attribute
        {
            Guard.ArgumentNotNull(assembly, "assembly");
            return GetAttributes(assembly, inherit).OfType<TAttribute>().ToArray();
        }

        /// <summary>
        /// Gets the attributes applied to the given <see cref="Type"/>,
        /// </summary>
        /// <typeparam name="TAttribute">The type of the attribute.</typeparam>
        /// <param name="type">The type.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>
        /// The attributes applied to the given <see cref="MemberInfo"/>
        /// </returns>
        public static IEnumerable<TAttribute> GetAttributes<TAttribute>(Type type, bool inherit) where TAttribute : Attribute
        {
            Guard.ArgumentNotNull(type, "type");;
            return GetAttributes(type, inherit).OfType<TAttribute>().ToArray();
        }

        /// <summary>
        /// Gets the attributes applied to the given <see cref="MemberInfo"/>.
        /// </summary>
        /// <typeparam name="TAttribute">The type of the attribute.</typeparam>
        /// <param name="member">The member.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>
        /// The attributes applied to the given <see cref="Type"/>
        /// </returns>
        public static IEnumerable<TAttribute> GetAttributes<TAttribute>(MemberInfo member, bool inherit) where TAttribute : Attribute
        {
            Guard.ArgumentNotNull(member, "member");
            return GetAttributes(member, inherit).OfType<TAttribute>().ToArray();
        }
        #endregion

        #region GetAttributes<T>
        private static IEnumerable<Attribute> GetAttributes<T>(T target, Func<T, bool, object[]> attributeAccessor,bool inherit)
        {
            Attribute[] attributes;
            if (!attributeCache.ContainsKey(target))
            {
                attributes = attributeAccessor(target, inherit).OfType<Attribute>().ToArray();
                lock (syncHelper)
                {
                    if (!attributeCache.ContainsKey(target))
                    {
                        attributeCache[target] = attributes;
                    }
                }
            }
            return attributeCache[target];
        }
        #endregion
    }
}
